package org.fisco.bcos.asset.client;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import org.fisco.bcos.asset.contract.CharityDonation;
import org.fisco.bcos.asset.resp.*;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple6;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.CryptoSuite;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.eventsub.EventCallback;
import org.fisco.bcos.sdk.model.TransactionReceipt;
import org.fisco.bcos.sdk.transaction.model.exception.ContractException;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple3;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple7;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

@Component
public class CharityDonationClient {

    static Logger logger = LoggerFactory.getLogger(AssetClient.class);
    private BcosSDK bcosSDK;
    private Client client;
    private CryptoKeyPair cryptoKeyPair;

    // 受助者3
//    String pemAccountFilePath = "src/main/resources/pem/0x710b45977cf6d125614258bd72963f2089937626.pem";
    // 监管机构2
//     String pemAccountFilePath = "src/main/resources/pem/0xb7fee8a2ab41344a024aa178f382d84e2075f6ab.pem";
    // 捐赠者4
     String pemAccountFilePath = "src/main/resources/pem/0xc99e9b338f2ca1e22ac6e9c605b34d58346afb09.pem";
    // 管理员1
//    String pemAccountFilePath = "src/main/resources/pem/0xded709141969aebdcd9e86a8c9be09a4176b73e5.pem";

    /**
     *
     * @throws Exception
     */
//    public void initialize() throws Exception {
//        @SuppressWarnings("resource")
//        ApplicationContext context =
//                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
//        // 获取客户端实例
//        bcosSDK = context.getBean(BcosSDK.class);
//        client = bcosSDK.getClient(1);
//        CryptoSuite cryptoSuite = client.getCryptoSuite();
//        cryptoSuite.loadAccount("pem", pemAccountFilePath, null);
//        // 获取当前加载的账户 TODO
//        cryptoKeyPair = cryptoSuite.getCryptoKeyPair();
//        logger.debug("create client for group1, account address is " + cryptoKeyPair.getAddress());
//    }
    public void initialize(String privateKey) throws Exception {
        @SuppressWarnings("resource")
        ApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        // 获取客户端实例
        bcosSDK = context.getBean(BcosSDK.class);
        client = bcosSDK.getClient(1);
        CryptoSuite cryptoSuite = client.getCryptoSuite();

        // 使用传入的私钥创建 CryptoKeyPair
        cryptoKeyPair = cryptoSuite.createKeyPair(privateKey);
        logger.debug("create client for group1, account address is " + cryptoKeyPair.getAddress());
    }

    public String deployCharityDonationAndRecordAddr() {

        try {
            CharityDonation charityDonation = CharityDonation.deploy(client, cryptoKeyPair);
            System.out.println(
                    " deploy CharityDonation success, contract address is " + charityDonation.getContractAddress());

            recordAssetAddr(charityDonation.getContractAddress());
            return charityDonation.getContractAddress();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            // e.printStackTrace();
            System.out.println(" deploy CharityDonation contract failed, error message is  " + e.getMessage());
        }
        return null;
    }
    public void recordAssetAddr(String address) throws FileNotFoundException, IOException {
        Properties prop = new Properties();
        prop.setProperty("address", address);
        final Resource contractResource = new ClassPathResource("contract.properties");
        FileOutputStream fileOutputStream = new FileOutputStream(contractResource.getFile());
        prop.store(fileOutputStream, "contract address");
    }

    public String loadCharityDonationAddr() throws Exception {
        // load Asset contact address from contract.properties
        Properties prop = new Properties();
        final Resource contractResource = new ClassPathResource("contract.properties");
        prop.load(contractResource.getInputStream());

        String contractAddress = prop.getProperty("address");
        if (contractAddress == null || contractAddress.trim().equals("")) {
            throw new Exception(" load CharityDonation contract address failed, please deploy it first. ");
        }
        logger.info(" load CharityDonation address from contract.properties, address is {}", contractAddress);
        return contractAddress;
    }

    // 2、设置监管者
    public void setRegulator(String regulatorAddress) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.setRegulator(regulatorAddress);
    }

    // 3、注册受益人
    public void registerBeneficiary(String beneficiaryAddress) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.registerBeneficiary(beneficiaryAddress);
    }

    // 4、提交申请
    public void submitApplication(String description, BigInteger requestedAmount) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        TransactionReceipt receipt = charityDonation.submitApplication(description, requestedAmount);
        List<CharityDonation.ApplicationSubmittedEventResponse> applicationSubmittedEvents = charityDonation.getApplicationSubmittedEvents(receipt);
        BigInteger applicationId = applicationSubmittedEvents.get(0).applicationId;
        System.out.println("Application submitted, applicationId: " + applicationId);
    }
    // 获取所有的受助者申请信息
    public List<Application> getAllApplications() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        Tuple6<List<BigInteger>, List<String>, List<String>, List<BigInteger>, List<Boolean>, List<BigInteger>> allApplications = charityDonation.getAllApplications();
        List<BigInteger> applicationIds = allApplications.getValue1();
        List<String> descriptions = allApplications.getValue3();
        List<String> donors = allApplications.getValue2();
        List<BigInteger> amounts = allApplications.getValue4();
        List<Boolean> approveds = allApplications.getValue5();
        List<BigInteger> timestamps = allApplications.getValue6();
        List<Application> applications = new ArrayList<>();
        for (int i = 0; i < applicationIds.size(); i++) {
            Application application = new Application();
            application.setApplicationId(applicationIds.get(i));
            application.setDescription(descriptions.get(i));
            application.setDonor(donors.get(i));
            application.setAmount(amounts.get(i));
            application.setApproved(approveds.get(i).toString());
            application.setTimestamp(timestamps.get(i));
            applications.add(application);
        }
        return applications;
    }
    // 5、审批申请
    public void approveApplication(BigInteger applicationId) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        TransactionReceipt receipt = charityDonation.approveApplication(applicationId);
        List<CharityDonation.ApplicationApprovedEventResponse> applicationApprovedEvents = charityDonation.getApplicationApprovedEvents(receipt);
        BigInteger applicationId1 = applicationApprovedEvents.get(0).applicationId;
        System.out.println("Application approved, applicationId: " + applicationId1);

    }

    // 6、创建捐赠项目
    public BuildProject createDonationProject(String projectName, String description,
                                                    String beneficiaryAddress, BigInteger targetAmount) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        TransactionReceipt receipt = charityDonation.createDonationProject(projectName, description, beneficiaryAddress, targetAmount);
        List<CharityDonation.ProjectCreatedEventResponse> responses = charityDonation.getProjectCreatedEvents(receipt);
        BuildProject buildProject = new BuildProject();
        buildProject.setProjectId(responses.get(0).projectId.toString());
        buildProject.setProjectName(responses.get(0).projectName);
        buildProject.setTargetAmount(responses.get(0).targetAmount);
        System.out.println(responses.get(0).projectId.toString());
        return buildProject;
    }

    // 查询所有待监管审核项目
    public List<PendingProject> getPendingProjects() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        Tuple7<List<BigInteger>, List<String>, List<String>, List<String>, List<BigInteger>, List<BigInteger>, List<BigInteger>> allPendingProjects = charityDonation.getAllPendingProjects();
        List<PendingProject> pendingProjects = new ArrayList<>();
        for (int i = 0; i < allPendingProjects.getValue1().size(); i++) {
            PendingProject pendingProject = new PendingProject();
            pendingProject.setProjectId(allPendingProjects.getValue1().get(i));
            pendingProject.setProjectName(allPendingProjects.getValue2().get(i));
            pendingProject.setDescription(allPendingProjects.getValue3().get(i));
            pendingProject.setBeneficiary(allPendingProjects.getValue4().get(i));
            pendingProject.setTargetAmount(allPendingProjects.getValue5().get(i));
            pendingProject.setRaisedAmount(allPendingProjects.getValue6().get(i));
            pendingProject.setCreatedAt(allPendingProjects.getValue7().get(i));
            pendingProjects.add(pendingProject);
        }
        return pendingProjects;
    }

    // 7、审批项目
    public void approveProject(BigInteger projectId) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);

        TransactionReceipt receipt = charityDonation.approveProject(projectId);
//        List<CharityDonation.ProjectApprovedEventResponse> response = charityDonation.getProjectApprovedEvents(receipt);
    }

    // TODO 查询所有的Active的项目
    public List<ActiveProject> getAllActiveProjects() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        Tuple7<List<BigInteger>, List<String>, List<String>, List<String>, List<BigInteger>, List<BigInteger>, List<BigInteger>> allActiveProjects = charityDonation.getAllActiveProjects();
        ArrayList<ActiveProject> activeProjects = new ArrayList<>();
        for (int i = 0; i < allActiveProjects.getValue1().size(); i++) {
            ActiveProject allActiveProject = new ActiveProject();
            allActiveProject.setProjectId(allActiveProjects.getValue1().get(i));
            allActiveProject.setProjectName(allActiveProjects.getValue2().get(i));
            allActiveProject.setDescription(allActiveProjects.getValue3().get(i));
            allActiveProject.setBeneficiary(allActiveProjects.getValue4().get(i));
            allActiveProject.setTargetAmount(allActiveProjects.getValue5().get(i));
            allActiveProject.setRaisedAmount(allActiveProjects.getValue6().get(i));
            allActiveProject.setCreatedAt(allActiveProjects.getValue7().get(i));
            activeProjects.add(allActiveProject);
        }
        return activeProjects;
    }

    // 8、捐赠
    public DonorRecord donate(BigInteger projectId, BigInteger amount) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        TransactionReceipt receipt = charityDonation.donate(projectId, amount);
        List<CharityDonation.DonationMadeEventResponse> response = charityDonation.getDonationMadeEvents(receipt);
        System.out.println(response.get(0).amount);
        System.out.println(response.get(0).donor);
        System.out.println(response.get(0).projectId);
        DonorRecord donorRecord = new DonorRecord();
        donorRecord.setAmount(response.get(0).amount);
        donorRecord.setDonor(response.get(0).donor);
        donorRecord.setProjectId(response.get(0).projectId.toString());
        return donorRecord;
    }

    // 9、查询项目详情
    public ProjectDetail getProjectDetails(BigInteger projectId) throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        Tuple7<String, String, String, BigInteger, BigInteger, BigInteger, Boolean> details = charityDonation.getProjectDetails(projectId);

        ProjectDetail detail = new ProjectDetail(details.getValue1(), details.getValue2(), details.getValue3(), details.getValue4(), details.getValue5(), details.getValue6(), details.getValue7());
        return detail;
    }

    // 10、查询捐赠记录
    public DonationRecord
    getDonationRecords(BigInteger projectId) throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        Tuple3<List<BigInteger>, List<BigInteger>, List<String>> records = charityDonation.getDonationRecords(projectId);
        // TODO
// 获取所有捐赠记录
        List<BigInteger> amounts = records.getValue1();      // 捐赠金额列表
        List<BigInteger> timestamps = records.getValue2();   // 捐赠时间列表
        List<String> donors = records.getValue3();           // 捐赠者地址列表
        DonationRecord recordsDTO = new DonationRecord(amounts, timestamps, donors);
        return recordsDTO;
    }

    // 11、查询受益人统计信息
    public DonationSummary
    getBeneficiaryStats(String beneficiaryAddress) throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        Tuple3<BigInteger, BigInteger, BigInteger> stats = charityDonation.getBeneficiaryStats(beneficiaryAddress);
        // 获取统计数据
        BigInteger totalAmount = stats.getValue1();      // 总接收金额
        BigInteger projectCount = stats.getValue3();     // 参与项目数
        DonationSummary donationSummary = new DonationSummary();
        donationSummary.setTotalAmount(totalAmount);
        donationSummary.setProjectCount(projectCount.intValue());
        return donationSummary;
    }

    // 12、查询用户角色
    public BigInteger getUserRole(String userAddress) throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        BigInteger role = charityDonation.userRoles(userAddress);
        switch (role.intValue()) {
            case 0:
                System.out.println("普通用户");
                break;
            case 1:
                System.out.println("管理员");
                break;
            case 2:
                System.out.println("监管者");
                break;
            case 3:
                System.out.println("慈善组织");
                break;
            case 4:
                System.out.println("受益人");
                break;
            case 5:
                System.out.println("捐赠者");
                break;
            default:
                System.out.println("未知角色");
                break;
        }
        return role;
    }

    // 13、查询项目总数
    public BigInteger getProjectCount() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        System.out.println("项目总数 :" + charityDonation.projectCount());
        return charityDonation.projectCount();
    }

    // 14、查询申请总数
    public BigInteger getApplicationCount() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        System.out.println("申请总数 :" + charityDonation.applicationCount());
        return charityDonation.applicationCount();
    }

    // 查询特定捐赠者的捐赠金额
    public BigInteger getDonationAmount(BigInteger projectId, String donorAddress) throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        System.out.println("捐赠金额 :" + charityDonation.getDonationAmount(projectId, donorAddress));
        return charityDonation.getDonationAmount(projectId, donorAddress);
    }

    // 获取合约地址
    public String getContractAddress() {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        return charityDonation.getContractAddress();
    }

    // 获取监管者地址
    public String getRegulator() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        System.out.println("监管者地址 :" + charityDonation.regulator());
        return charityDonation.regulator();
    }

    // 获取慈善组织地址
    public String getCharityOrg() throws ContractException {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        return charityDonation.charityOrg();
    }

    // 事件监听相关方法
    public void subscribeProjectCreatedEvent(EventCallback callback) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.subscribeProjectCreatedEvent(callback);
    }

    public void subscribeDonationMadeEvent(EventCallback callback) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.subscribeDonationMadeEvent(callback);
    }

    public void subscribeApplicationSubmittedEvent(EventCallback callback) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.subscribeApplicationSubmittedEvent(callback);
    }

    public void subscribeApplicationApprovedEvent(EventCallback callback) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.subscribeApplicationApprovedEvent(callback);
    }

    public void subscribeProjectApprovedEvent(EventCallback callback) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.subscribeProjectApprovedEvent(callback);
    }

    public void subscribeFundsReleasedEvent(EventCallback callback) {
        String contractAddress = null;
        try {
            contractAddress = loadCharityDonationAddr();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        CharityDonation charityDonation = CharityDonation.load(contractAddress, client, cryptoKeyPair);
        charityDonation.subscribeFundsReleasedEvent(callback);
    }
}